package star.oath.framework.util;

import java.time.*;
import java.time.format.*;
import java.time.temporal.*;
import java.util.*;

public class DatetimeUtils {

    /**
     * 获取当前日期时间（格式：yyyy-MM-dd HH:mm:ss）
     *
     * @return 当前日期时间的字符串表示
     */
    public static String getCurrentDateTime() {
        return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
    }

    /**
     * 获取当前日期（格式：yyyy-MM-dd）
     *
     * @return 当前日期的字符串表示
     */
    public static String getCurrentDate() {
        return LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
    }

    /**
     * 获取当前年月（格式：yyyy-MM）
     *
     * @return 当前年月的字符串表示
     */
    public static String getCurrentYearMonth() {
        return LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM"));
    }

    /**
     * 获取当前年份
     *
     * @return 当前年份的字符串表示
     */
    public static String getCurrentYear() {
        return String.valueOf(LocalDate.now().getYear());
    }

    /**
     * 获取当前年份（整数形式）
     *
     * @return 当前年份的整数表示
     */
    public static int getCurrentYearAsInt() {
        return LocalDate.now().getYear();
    }

    /**
     * 获取当前月份（字符串形式，如"01","02"）
     *
     * @return 当前月份的字符串表示
     */
    public static String getCurrentMonth() {
        return String.format("%02d", getCurrentMonthAsInt());
    }

    /**
     * 获取当前月份（数字形式）
     *
     * @return 当前月份（1-12）
     */
    public static int getCurrentMonthAsInt() {
        return LocalDate.now().getMonthValue();
    }

    /**
     * 获取当前日期在周中的天数（周一为1，周日为7）
     *
     * @return 当前日期在周中的天数（1-7）
     */
    public static int getCurrentDayOfWeek() {
        DayOfWeek dayOfWeek = LocalDate.now().getDayOfWeek();
        return dayOfWeek.getValue(); // 周一返回1，周日返回7
    }

    /**
     * 获取当前日期在月份中的天数
     *
     * @return 当前日期在月份中的天数（1-31）
     */
    public static int getCurrentDayOfMonth() {
        return LocalDate.now().getDayOfMonth();
    }

    /**
     * 获取当前日期在年中的天数
     *
     * @return 当前日期在年中的天数（1-366）
     */
    public static int getCurrentDayOfYear() {
        return LocalDate.now().getDayOfYear();
    }

    /**
     * 将java.util.Date转换为LocalDate
     */
    private static LocalDate dateToLocalDate(Date date) {
        return LocalDate.ofInstant(date.toInstant(), java.time.ZoneId.systemDefault());
    }

    /**
     * 将LocalDate转换为java.util.Date
     */
    private static Date localDateToDate(LocalDate localDate) {
        return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
    }

    /**
     * 获取指定日期n天后的日期
     *
     * @param date 基准日期
     * @param days 增加的天数
     * @return 计算后的日期
     */
    public static Date addDays(Date date, int days) {
        LocalDate localDate = dateToLocalDate(date);
        LocalDate result = localDate.plusDays(days);
        return localDateToDate(result);
    }

    /**
     * 获取指定日期n个月后的日期（日期相同，月份增加）
     *
     * @param date   基准日期
     * @param months 增加的月数
     * @return 计算后的日期，如果目标月份没有对应日期，则使用该月最后一天
     */
    public static Date addMonths(Date date, int months) {
        LocalDate localDate = dateToLocalDate(date);
        LocalDate result = localDate.plusMonths(months);
        return localDateToDate(result);
    }

    /**
     * 获取指定日期n年后的日期（日期相同，年份增加）
     *
     * @param date  基准日期
     * @param years 增加的年数
     * @return 计算后的日期，如果目标年份没有对应日期，则使用该月最后一天
     */
    public static Date addYears(Date date, int years) {
        LocalDate localDate = dateToLocalDate(date);
        LocalDate result = localDate.plusYears(years);
        return localDateToDate(result);
    }

    /**
     * 获取指定日期字符串n天后的日期字符串
     *
     * @param dateStr 基准日期字符串（格式：yyyy-MM-dd）
     * @param days    增加的天数
     * @return 计算后的日期字符串
     */
    public static String addDays(String dateStr, int days) {
        LocalDate localDate = LocalDate.parse(dateStr);
        LocalDate result = localDate.plusDays(days);
        return result.toString();
    }

    /**
     * 获取指定日期字符串n个月后的日期字符串
     *
     * @param dateStr 基准日期字符串（格式：yyyy-MM-dd）
     * @param months  增加的月数
     * @return 计算后的日期字符串，如果目标月份没有对应日期，则使用该月最后一天
     */
    public static String addMonths(String dateStr, int months) {
        LocalDate localDate = LocalDate.parse(dateStr);
        LocalDate result = localDate.plusMonths(months);
        return result.toString();
    }

    /**
     * 获取指定日期字符串n年后的日期字符串
     *
     * @param dateStr 基准日期字符串（格式：yyyy-MM-dd）
     * @param years   增加的年数
     * @return 计算后的日期字符串，如果目标年份没有对应日期，则使用该月最后一天
     */
    public static String addYears(String dateStr, int years) {
        LocalDate localDate = LocalDate.parse(dateStr);
        LocalDate result = localDate.plusYears(years);
        return result.toString();
    }

    /**
     * 计算两个日期之间的天数差值
     *
     * @param date1 第一个日期
     * @param date2 第二个日期
     * @return 天数差值，date1晚于date2时返回正数
     */
    public static long getDaysBetween(Date date1, Date date2) {
        LocalDate localDate1 = dateToLocalDate(date1);
        LocalDate localDate2 = dateToLocalDate(date2);
        return ChronoUnit.DAYS.between(localDate1, localDate2);
    }

    /**
     * 计算两个日期之间的天数差值（字符串格式）
     *
     * @param dateStr1 第一个日期字符串（格式：yyyy-MM-dd）
     * @param dateStr2 第二个日期字符串（格式：yyyy-MM-dd）
     * @return 天数差值
     */
    public static long getDaysBetween(String dateStr1, String dateStr2) {
        LocalDate date1 = LocalDate.parse(dateStr1);
        LocalDate date2 = LocalDate.parse(dateStr2);
        return ChronoUnit.DAYS.between(date1, date2);
    }

    /**
     * 比较两个Date对象，判断第一个时间是否晚于第二个时间
     *
     * @param firstDate  第一个时间
     * @param secondDate 第二个时间
     * @return 如果第一个时间晚于第二个时间，返回true；否则返回false
     */
    public static boolean isAfter(Date firstDate, Date secondDate) {
        if (firstDate == null || secondDate == null) {
            throw new IllegalArgumentException("日期参数不能为空");
        }
        return firstDate.after(secondDate);
    }

    /**
     * 比较两个LocalDate对象，判断第一个日期是否晚于第二个日期
     *
     * @param firstDate  第一个日期
     * @param secondDate 第二个日期
     * @return 如果第一个日期晚于第二个日期，返回true；否则返回false
     */
    public static boolean isAfter(LocalDate firstDate, LocalDate secondDate) {
        if (firstDate == null || secondDate == null) {
            throw new IllegalArgumentException("日期参数不能为空");
        }
        return firstDate.isAfter(secondDate);
    }

    /**
     * 比较两个LocalDateTime对象，判断第一个时间是否晚于第二个时间
     *
     * @param firstDateTime  第一个时间
     * @param secondDateTime 第二个时间
     * @return 如果第一个时间晚于第二个时间，返回true；否则返回false
     */
    public static boolean isAfter(LocalDateTime firstDateTime, LocalDateTime secondDateTime) {
        if (firstDateTime == null || secondDateTime == null) {
            throw new IllegalArgumentException("日期时间参数不能为空");
        }
        return firstDateTime.isAfter(secondDateTime);
    }

    /**
     * 比较两个ZonedDateTime对象，判断第一个时间是否晚于第二个时间
     *
     * @param firstZonedDateTime  第一个带时区的时间
     * @param secondZonedDateTime 第二个带时区的时间
     * @return 如果第一个时间晚于第二个时间，返回true；否则返回false
     */
    public static boolean isAfter(ZonedDateTime firstZonedDateTime, ZonedDateTime secondZonedDateTime) {
        if (firstZonedDateTime == null || secondZonedDateTime == null) {
            throw new IllegalArgumentException("带时区的日期时间参数不能为空");
        }
        return firstZonedDateTime.isAfter(secondZonedDateTime);
    }

    /**
     * 比较两个字符串表示的日期，使用指定的格式
     *
     * @param firstDateStr  第一个日期字符串
     * @param secondDateStr 第二个日期字符串
     * @param format        日期格式，如："yyyy-MM-dd"
     * @return 如果第一个日期晚于第二个日期，返回true；否则返回false
     */
    public static boolean isAfterDate(String firstDateStr, String secondDateStr, String format) {
        if (firstDateStr == null || secondDateStr == null || format == null) {
            throw new IllegalArgumentException("日期字符串或格式参数不能为空");
        }
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
        LocalDate firstDate = LocalDate.parse(firstDateStr, formatter);
        LocalDate secondDate = LocalDate.parse(secondDateStr, formatter);
        return isAfter(firstDate, secondDate);
    }

    /**
     * 比较两个字符串表示的日期时间，使用指定的格式
     *
     * @param firstDateTimeStr  第一个日期时间字符串
     * @param secondDateTimeStr 第二个日期时间字符串
     * @param format            日期时间格式，如："yyyy-MM-dd HH:mm:ss"
     * @return 如果第一个时间晚于第二个时间，返回true；否则返回false
     */
    public static boolean isAfterDateTime(String firstDateTimeStr, String secondDateTimeStr, String format) {
        if (firstDateTimeStr == null || secondDateTimeStr == null || format == null) {
            throw new IllegalArgumentException("日期时间字符串或格式参数不能为空");
        }
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
        LocalDateTime firstDateTime = LocalDateTime.parse(firstDateTimeStr, formatter);
        LocalDateTime secondDateTime = LocalDateTime.parse(secondDateTimeStr, formatter);
        return isAfter(firstDateTime, secondDateTime);
    }

    /**
     * 比较两个Date对象，判断第一个时间是否早于第二个时间
     *
     * @param firstDate  第一个时间
     * @param secondDate 第二个时间
     * @return 如果第一个时间早于第二个时间，返回true；否则返回false
     */
    public static boolean isBefore(Date firstDate, Date secondDate) {
        if (firstDate == null || secondDate == null) {
            throw new IllegalArgumentException("日期参数不能为空");
        }
        return firstDate.before(secondDate);
    }

    /**
     * 比较两个LocalDate对象，判断第一个日期是否早于第二个日期
     *
     * @param firstDate  第一个日期
     * @param secondDate 第二个日期
     * @return 如果第一个日期早于第二个日期，返回true；否则返回false
     */
    public static boolean isBefore(LocalDate firstDate, LocalDate secondDate) {
        if (firstDate == null || secondDate == null) {
            throw new IllegalArgumentException("日期参数不能为空");
        }
        return firstDate.isBefore(secondDate);
    }

    /**
     * 比较两个LocalDateTime对象，判断第一个时间是否早于第二个时间
     *
     * @param firstDateTime  第一个时间
     * @param secondDateTime 第二个时间
     * @return 如果第一个时间早于第二个时间，返回true；否则返回false
     */
    public static boolean isBefore(LocalDateTime firstDateTime, LocalDateTime secondDateTime) {
        if (firstDateTime == null || secondDateTime == null) {
            throw new IllegalArgumentException("日期时间参数不能为空");
        }
        return firstDateTime.isBefore(secondDateTime);
    }

    /**
     * 比较两个ZonedDateTime对象，判断第一个时间是否早于第二个时间
     *
     * @param firstZonedDateTime  第一个带时区的时间
     * @param secondZonedDateTime 第二个带时区的时间
     * @return 如果第一个时间早于第二个时间，返回true；否则返回false
     */
    public static boolean isBefore(ZonedDateTime firstZonedDateTime, ZonedDateTime secondZonedDateTime) {
        if (firstZonedDateTime == null || secondZonedDateTime == null) {
            throw new IllegalArgumentException("带时区的日期时间参数不能为空");
        }
        return firstZonedDateTime.isBefore(secondZonedDateTime);
    }

    /**
     * 比较两个字符串表示的日期，使用指定的格式
     * 判断第一个时间是否早于第二个日期
     *
     * @param firstDateStr  第一个日期字符串
     * @param secondDateStr 第二个日期字符串
     * @param format        日期格式，如："yyyy-MM-dd"
     * @return 如果第一个时间早于第二个时间，返回true；否则返回false
     */
    public static boolean isBeforeDate(String firstDateStr, String secondDateStr, String format) {
        if (firstDateStr == null || secondDateStr == null || format == null) {
            throw new IllegalArgumentException("日期字符串或格式参数不能为空");
        }
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
        LocalDate firstDate = LocalDate.parse(firstDateStr, formatter);
        LocalDate secondDate = LocalDate.parse(secondDateStr, formatter);
        return isBefore(firstDate, secondDate);
    }

    /**
     * 比较两个字符串表示的日期时间，使用指定的格式
     * 判断第一个时间是否早于第二个时间
     *
     * @param firstDateTimeStr  第一个日期时间字符串
     * @param secondDateTimeStr 第二个日期时间字符串
     * @param format            日期时间格式，如："yyyy-MM-dd HH:mm:ss"
     * @return 如果第一个时间早于第二个时间，返回true；否则返回false
     */
    public static boolean isBeforeDateTime(String firstDateTimeStr, String secondDateTimeStr, String format) {
        if (firstDateTimeStr == null || secondDateTimeStr == null || format == null) {
            throw new IllegalArgumentException("日期时间字符串或格式参数不能为空");
        }
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
        LocalDateTime firstDateTime = LocalDateTime.parse(firstDateTimeStr, formatter);
        LocalDateTime secondDateTime = LocalDateTime.parse(secondDateTimeStr, formatter);
        return isBefore(firstDateTime, secondDateTime);
    }

    /**
     * 判断指定年份是否为闰年
     *
     * @param year 指定年份
     * @return 如果是闰年返回 true，否则返回 false
     */
    public static boolean isLeapYear(int year) {
        return Year.isLeap(year);
    }

}
